package org.cainiao.servlet.workflow.delegate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.cainiao.servlet.workflow.dto.Param;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CallExternalSystemDelegate implements JavaDelegate {

    private ObjectMapper mapper = new ObjectMapper();

    @Override
    public void execute(DelegateExecution execution) {
        System.out.println();
        /*
         [
            { "key": "personId", "type": "SP_EL", "value": "#root['person'].id" },
            {
                "key": "data",
                "type": "MAP",
                "children": [
                    { "key": "name", "type": "SP_EL", "value": "#root['person'].username" }
                ]
            },
            {
                "key": "personHobbies",
                "type": "ARRAY",
                "value": "#root['person'].hobbies",
                "children": [
                    { "key": "hobbyName", "type": "SP_EL", "value": "#root['element'].name" }
                ]
            }
         ]

         [{&quot;key&quot;:&quot;personId&quot;,&quot;type&quot;:&quot;SP_EL&quot;,&quot;value&quot;:&quot;#root['person'].id&quot;},{&quot;key&quot;: &quot;data&quot;,&quot;type&quot;:&quot;MAP&quot;,&quot;children&quot;:[{&quot;key&quot;:&quot;name&quot;,&quot;type&quot;:&quot;SP_EL&quot;,&quot;value&quot;:&quot;#root['person'].username&quot;}]},{&quot;key&quot;:&quot;personHobbies&quot;,&quot;type&quot;:&quot;ARRAY&quot;,&quot;value&quot;:&quot;#root['person'].hobbies&quot;,&quot;children&quot;:[{&quot;key&quot;:&quot;hobbyName&quot;,&quot;type&quot;:&quot;SP_EL&quot;,&quot;value&quot;:&quot;#root['element'].name&quot;}]}]
         ->
         {
            "personId": "#root['person'].id",
            "data": { "name": "#root['person'].username" },
            "personHobbies": [{ "hobbyName": "#root['element'].name" }]
         }
         */
        String parameterTemplate = execution.getCurrentFlowElement()
                .getAttributeValue("http://flowable.org/bpmn", "parameter");
        System.out.println(parameterTemplate);

        Map<String, Object> parameter = getParameter(execution.getVariables(), parameterTemplate);
        System.out.println(parameter);
    }

    private Map<String, Object> getParameter(Map<String, Object> variables, String parameterTemplate) {
        try {
            List<Param> paramTemplates = mapper.readValue(parameterTemplate, new TypeReference<>() {
            });
            return getParameter(variables, paramTemplates);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, Object> getParameter(Map<String, Object> variables, List<Param> paramTemplates) {
        Map<String, Object> param = new HashMap<>();
        paramTemplates.forEach(paramTemplate -> {
            switch (paramTemplate.getType()) {
                case SP_EL -> param.put(paramTemplate.getKey(), getValue(variables, paramTemplate.getValue()));
                case MAP -> param.put(paramTemplate.getKey(), getParameter(variables, paramTemplate.getChildren()));
                case ARRAY -> {
                    Object arrayObject = getValue(variables, paramTemplate.getValue());
                    if (arrayObject instanceof List<?> array) {
                        List<Param> children = paramTemplate.getChildren();
                        param.put(paramTemplate.getKey(), array.stream().map(element -> {
                            Map<String, Object> data = new HashMap<>();
                            data.put("element", element);
                            return getParameter(data, children);
                        }).collect(Collectors.toList()));
                    } else {
                        System.err.printf("%s 的类型不匹配，应该为数组", paramTemplate.getKey());
                    }
                }
            }
        });
        return param;
    }

    private Object getValue(Map<String, Object> data, String spEL) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setRootObject(data);
        return new SpelExpressionParser().parseExpression(spEL).getValue(context);
    }
}
